// javamaps
// Copyright (c) 2010 Alina Belei
package Utils.LocationFinder;

import java.util.*;
import Core.*;
import javax.swing.*;


class TrieEntry  {
  private String key_;
  private Object data_ = null;
  private Street street_ = null;

  public TrieEntry() { this("",null, null); }

  public TrieEntry(String key,Object data, Street str ) {
    setKey(key);
    setData(data);
    setStreet(str);
  }

  public String Key() { return key_; }

  public void setKey(String key) { key_ = key; }

  public Object Data() { return data_; }

  public void setData(Object data) { data_ =data; }

  public Street Street() { return street_; }

  public void setStreet(Street str) { street_ = str; }

}


public class Trie extends JPanel{

 /*
  * Variabile, campuri
  */
  //informatii privid locatia acestui Trie in arborele Trie
  int position_;
  Trie previous_;

  TrieEntry entry_; // Pentru atunci cand un sir se termina in acest trie

  Map entries_;//Elementele acestui trie


  List<String> liststr;
  List<String> ls=new ArrayList();
  int i;


/*
 * Constructori.
 */

  //Creaza un nou trie cu nici o intrare
  public Trie() {
    entries_ = new HashMap();
    entry_ = null;
    position_ = 0;
    previous_ = null;

  }

  // Constructor intern
  private Trie(Trie parent) {
    this();
    previous_ = parent;
    position_ = parent.getPosition() + 1;
  }

 /*
  * Get si set
  */

  // Returneaza offsetul caracterului acestui trie care nu mai apartine arbororelui principal
  int getPosition() { return (position_); }

  public Map Entries() { return entries_;}

  public void setEntries(Map entries) { entries_ = entries; }

  public TrieEntry Entry() { return entry_; }

  public void setEntry(TrieEntry entry) { entry_ = entry; }

  public Trie Previous() {  return previous_; }

  public void setPrevious(Trie previous) {  previous_ = previous; }

 /*
  * Alte metode
  */

  public String toString() {
    if (length() > 20) {
        return ("Multiple matches: <too many to list (>20)>");
    }
    else {
        return ("Multiple matches: " + contents(new StringBuffer()));
    }
  }

    //Returneaza un trie pentru mai multe optiuni, sau returneaza null daca nici un obiect nu s-a potrivit, sau obiectul in sine
  public Object search(String key) {
    if (key.length() == getPosition()) {
      if (entry_ != null) {    return (entry_.Data());  }
      else {   return (this); }
      }
    String positionKey = getPositionKey(key);
    if (!Entries().containsKey(positionKey)) {
      if (entry_ != null) {  return (entry_.Data());   }
      else { return (null);    }
     }
    else {
      Object object = Entries().get(positionKey);

      if (object instanceof Trie) { return (((Trie) object).search(key));   }
      else {
         if (((TrieEntry) object).Key().startsWith(key)) { return (((TrieEntry) object).Data());         }
         else {   return (null);    }
       }
    }
  }

 // Nu returneaza un trie daca exista mai multe optiuni, va returna null
  public Object searchExact(String key) {
    if (key.length() == position_) {
      if (entry_ != null) {
          return (entry_.Street());
      }
      else {
          return (null);
      }
    }

    String positionKey = getPositionKey(key);

    if (!Entries().containsKey(positionKey)) {
      if (entry_ != null) {
          return (entry_.Street());
      }
      else {
          return (null);
      }
    }
    else {
      Object object = Entries().get(positionKey);

      if (object instanceof Trie) {
          return (((Trie) object).searchExact(key));
      }
      else {
        if ((position_ + 1) < key.length()) {
          if (key.substring(0, position_ + 1).equalsIgnoreCase(((TrieEntry) object).Key())) {
            return (((TrieEntry) object).Street());
          }
        }

       if (key.equalsIgnoreCase(((TrieEntry) object).Key())) {
          return (((TrieEntry) object).Street());
        }
       else {
            return (null);
       }
      }
    }
  }

 //Insereaza obiectul cu valoarea data in trie
  public void insert(String key, Object item, Street str) throws NonUniqueKeyException {
    if (key.length() == position_) {    // acest obiect trebuie sa fie inserat in acest trie
      if (entry_ != null) {
          throw new NonUniqueKeyException(key);
      }
      entry_ = new TrieEntry(key, item, str);
    }
    else {
      String positionKey = getPositionKey(key);

      if (Entries().containsKey(positionKey)) {    // este deja un obiect aici
        Object object = Entries().get(positionKey);
        if (object instanceof Trie && (((Trie) object).previous_ == this)) {
          ((Trie) object).insert(key, item, str);
          return;
        }
        else {// aici este deja ceva, se creaza un nou trie in care se insereaza obiectul existent si noul obiect ce se doreste inserat
          TrieEntry entry = (TrieEntry) object;
          if (entry.Key().equals(key)) {
              throw new NonUniqueKeyException(key);
          }

          Trie down = new Trie(this);
          down.insert(entry.Key(), entry.Data(), entry.Street());
          down.insert(key, item,str);

          Entries().put(positionKey, down);
        }
      }
     else {    //  spatiul este gol, se adauga pur si simplu obiectul
        Entries().put(positionKey, new TrieEntry(key, item, str));
      }
    }
  }

  void quickDump() {
    if (entry_ != null) {
         System.out.println(entry_.Key());
 
    }

    if (Entries() != null && Entries().size() > 0) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();

        if (object instanceof Trie && (((Trie) object).previous_ == this)) {
            ((Trie) object).quickDump();
        }
        else {
            if((TrieEntry) object!=null){
            System.out.println(((TrieEntry) object).Key());
            }
 
        }

      }

    }
  }

 void qD(List<String> ls) {
    if(entry_!=null)ls.add(entry_.Key());
    if (Entries() != null ) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();

        if (object instanceof Trie && (((Trie) object).previous_ == this)) {
            ((Trie) object).qD(ls);
        }
        else {
              ls.add(((TrieEntry) object).Key());
        }
      }
    }
  }

List<String> listsugestions(String text){
        Object result=null;
        List<String> list = new ArrayList<String>();
        
        i=0;
        result=search(text);
        if (result instanceof Trie) {
          Trie trie = (Trie) result;
          trie.qD(list);
        }
        else{
            list.add((String)result);
        }

        return list;
 }


  // Numara obiectele legate de acest trie
  public int length() {
    int count = 0;

    if (Entry() != null) {
        count++;
    }

    if (Entries() != null && Entries().size() > 0) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();
        if (object instanceof TrieEntry) {
            count++;
        }
        else {
            count += ((Trie) object).length();
        }
      }
    }
    return (count);
  }

  //Pune elementele arborelui curent intr-un StringBuffer
  public String contents(StringBuffer buffer) {
    if (Entry() != null) {
      buffer.append(Entry().Key());
      buffer.append(" ");
    }

    if (Entries() != null && Entries().size() > 0) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();

        if (object instanceof Trie && (((Trie) object).Previous() == this)) {
          buffer.append(((Trie) object).contents());
        }
        else {
          buffer.append(((TrieEntry) object).Key());
          buffer.append(" ");
        }
      }
    }
    return (buffer.toString());
  }


  public String contents() {
    return (contents(new StringBuffer()));
  }

  void dump() {
    String pre = spaces();
    if (Entry() != null) {
        System.out.println(pre + Entry().Key());
    }

    if (Entries() != null && Entries().size() > 0) {
      Iterator keyIterator = Entries().keySet().iterator();
      Iterator entryIterator = Entries().values().iterator();

      while (keyIterator.hasNext() && entryIterator.hasNext()) {
        String key = (String) keyIterator.next();
        Object object = entryIterator.next();

        if (object instanceof Trie && (((Trie) object).Previous() == this)) {
          System.out.println(pre + "[" + key + "]");
          ((Trie) object).dump();
        }
        else {
            System.out.println(pre + ((TrieEntry) object).Key());
        }
      }
    }
  }

  String spaces() {
    StringBuffer s = new StringBuffer();

    for (int index = 0; index < getPosition(); index++){
        s.append(" ");
    }

    return (s.toString());
  }

  // Returneaza o lista sortata alfabetic cu elementele din acest trie
  public LinkedList getSortedList() {
    LinkedList ll = new LinkedList();
    listElements(ll);
    return (ll);
  }

  // Functie recursiva de adaugare a elementelor intr-un linked list
  protected void listElements(LinkedList ll) {
    if (Entry() != null) {
        ll.addLast(Entry().Data());
    }

    if (Entries() != null && Entries().size() > 0) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();

        if (object instanceof Trie && (((Trie) object).Previous() == this)) {
            ((Trie) object).listElements(ll);
        }
        else {
            ll.addLast(((TrieEntry) object).Data());
        }
      }
    }
  }


  //Returneaza un trie cu toate elementele care incep cu sirul dat ca si parametru, sau un obiect care este exact la fel
  public Object getTrieFor(String key) {
    if (key.length() == position_) {
        return (this);
    }

    String positionKey = getPositionKey(key);

    if (Entries().containsKey(positionKey)) {
      Object object = Entries().get(positionKey);

      if (object instanceof Trie) {
          return (((Trie) object).getTrieFor(key));
      }
      else {
        if (((TrieEntry) object).Key().startsWith(key.toLowerCase())) {
          return (((TrieEntry) object).Data());
        }
      }
    }

    return (null);
  }

  private String getPositionKey(String key) {
    int startPos = position_;
    int endPos = (position_ + 1 <= key.length() ? position_ + 1 : position_);

    String positionKey = key.substring(startPos, endPos);
    return positionKey;
  }

 

  // Se sterge obiectul cu valoarea specificata din trie
  public void remove(String key) throws NonUniqueKeyException, NoSuchElementException {
    if (key.length() == getPosition()) {
      // obiectul este la 'entry_' pentru acest trie
      if (Entry() != null) {
          setEntry(null); return;
      }
      else {
          throw new NonUniqueKeyException(key);
      }
    }
    else {
      String positionKey = getPositionKey(key);

      if (Entries().containsKey(positionKey)) {
        Object object = Entries().get(positionKey);


        if (object instanceof Trie && ((Trie) object).Previous() == this) {
          Trie trie = ((Trie) object);
          trie.remove(key);

          int subTrieSize = trie.length();
          if (subTrieSize == 1) {
            TrieEntry entry = trie.firstElement();
            if (entry != null) {
                Entries().put(positionKey, entry);
            }
            else {
                Entries().remove(positionKey);
            }
          }
        }
        else {
          TrieEntry entry = (TrieEntry) object;
          if (entry.Key().equals(key)) {
              Entries().remove(positionKey);
          }
          return;
        }
      }
      else {
          throw new java.util.NoSuchElementException(key);
      }
    }
  }

  //Returneaza primul element din trie
  TrieEntry firstElement() {
    if (entry_ != null) { return entry_; }

    if (Entries() != null && Entries().size() > 0) {
      Iterator iterator = Entries().values().iterator();

      while (iterator.hasNext()) {
        Object object = iterator.next();
        if (!(object instanceof Trie && (((Trie) object).previous_ == this))) {
            return ((TrieEntry) object);
        }
      }
    }

    return (null);
  }

}